package com.jeomo.base.provider.aspect;

import com.jeomo.common.core.annocation.DictField;
import com.jeomo.common.core.annocation.DictLabel;
import com.jeomo.common.core.result.CommonResult;
import com.jeomo.common.core.util.ClassUtil;
import com.jeomo.common.core.util.StringUtils;
import com.jeomo.sys.starter.entity.Dict;
import com.jeomo.sys.starter.service.IDictService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * 字典切面类
 * @Author: qbt
 * @Date: 2020/8/30 20:10
 * @Version 1.0
 */

@Aspect
@Component
public class DictDataAspect extends ApplicationObjectSupport {

    Logger logger = LoggerFactory.getLogger(DictDataAspect.class);

    @Resource
    IDictService dictService;

    /**
     * 定义切面
     */
    @Pointcut("@annotation(com.jeomo.common.core.annocation.DataDict)")
    public void doDictDataAspect(){}

    @Around(value = "doDictDataAspect()")
    public Object translateDict(ProceedingJoinPoint point) throws Throwable {
        return parseData(point.proceed());
    }

    private Object parseData(Object object) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        if(object == null) {
            return null;
        }
        if(object instanceof CommonResult) {
            CommonResult cr = (CommonResult) object;
            cr.setData(parseData(cr.getData()));
        } else {
            parseDataDict(object);
        }
        return object;
    }

    private void parseDataDict(Object obj) throws IllegalAccessException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
        if(obj == null) {
            return;
        }
        Class clazz = obj.getClass();
        Field[] fields = ClassUtil.getDeclaredFields(clazz);
        for (Field field : fields) {
            field.setAccessible(true);
            if(!ClassUtil.hasGetMethod(clazz, field.getName())) {
                continue;
            }
            if(field.isAnnotationPresent(DictField.class)) {
                DictField df = field.getAnnotation(DictField.class);
                String dictType = df.type();
                String beanName = df.beanName();
                String methodName = df.methodName();
                String sourceFieldName = df.sourceField();
                Field sourceField = clazz.getDeclaredField(sourceFieldName);
                sourceField.setAccessible(true);
                Object sourceValue = sourceField.get(obj);
                if(sourceValue == null) {
                    continue;
                }
                Object dictValue = translateDictValue(field.getType(), sourceValue, dictType, beanName, methodName);
                field.set(obj, dictValue);
            } else if(!field.getDeclaringClass().getName().startsWith("java.lang")
                    || !Modifier.isFinal(field.getModifiers())
                    || !Modifier.isStatic(field.getModifiers())
            ) {
                Object fieldValue = field.get(obj);
                if(fieldValue == null) {
                    continue;
                } else {
                    if(fieldValue instanceof Collection) {
                        Collection c = (Collection) fieldValue;
                        Iterator iterator = c.iterator();
                        while(iterator.hasNext()) {
                            parseDataDict(iterator.next());
                        }
                    } else {
                        parseDataDict(fieldValue);
                    }
                }
            }
        }
    }

    private Object translateDictValue(Class fieldClass, Object sourceValue, String dictType, String beanName, String methodName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
        if(StringUtils.isNotEmpty(dictType)) {
            if(sourceValue instanceof Collection) {
                List<String>vs = new ArrayList<>();
                Collection fc = (Collection) sourceValue;
                for (Object o : fc) {
                    Dict dict = dictService.queryByCode(dictType, o.toString());
                    if(dict != null) {
                        vs.add(dict.getLabel());
                    }
                }
                return vs;
            } else {
                Dict dict = dictService.queryByCode(dictType, sourceValue.toString());
                if(dict != null) {
                    return dict.getLabel();
                }
            }
        } else if (StringUtils.isNotEmpty(beanName)) {
            Object bean = super.getApplicationContext().getBean(beanName);
            Class<?> beanClass = bean.getClass();
            Method method = null;
            try {
                method = beanClass.getMethod(methodName, sourceValue.getClass());
            } catch (NoSuchMethodException e ) {
                method = beanClass.getMethod(methodName, Serializable.class);
            }

            method.setAccessible(true);
            Object o = method.invoke(bean, sourceValue);
            if(o == null){
                return null;
            }
            // 如果返回类型正好是字段类型，则直接返回
            if(o.getClass().equals(fieldClass)) {
                return o;
            }
            //如果返回类型不是字段类型，则找出o的含有DictLabel的字段，并返回其值
            List<Field> labelFields = ClassUtil.getFieldsListWithAnnotation(o.getClass(), DictLabel.class);
            if(labelFields != null && labelFields.size() > 0) {
                Field labelField = labelFields.get(0);
                Object fieldValue = ClassUtil.getFiledValue(o, labelField.getName());
                return fieldValue;
            }
            return o;
        }
        return null;
    }

}
